﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using MiscUtil;

namespace TlvLib.SiChuan
{
    class SendHandler
    {
        private readonly ConcurrentDictionary<uint, CarInfo> _carInfosHistory;
        private readonly BlockingCollection<CarInfo> _carInfosToBeSend = new BlockingCollection<CarInfo>();
        private Stream _stream;
        private bool _started;
        private System.Threading.CancellationTokenSource _cts;
        private Thread _thread;
        private NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();

        public event EventHandler ConnectionClosed;
        public event EventHandler<MiscUtil.EventArgs<Exception>> ErrorOccurred;

        public void OnErrorOccurred(EventArgs<Exception> e)
        {
            EventHandler<EventArgs<Exception>> handler = ErrorOccurred;
            if (handler != null) handler(this, e);
        }

        public void OnConnectionClosed(EventArgs e)
        {
            EventHandler handler = ConnectionClosed;
            if (handler != null) handler(this, e);
        }

        public SendHandler(ConcurrentDictionary<uint, CarInfo> carInfos, BlockingCollection<CarInfo> carInfosToBeSend)
        {
            if (carInfos == null) throw new ArgumentNullException("carInfos");
            _carInfosHistory = carInfos;
            _carInfosToBeSend = carInfosToBeSend;
        }

        public void Start(Stream stream)
        {
            if (stream == null) throw new ArgumentNullException("stream");
            _stream = stream;

            if (_thread == null)
            {
                _cts = new CancellationTokenSource();
                _thread = new Thread(Worker);
                _thread.IsBackground = true;
                _thread.Start();
                _started = true;
            }
        }

        public void Stop()
        {
            if (_cts != null)
            {
                _cts.Cancel();
            }
        }

        private void Worker(object state)
        {
            DateTime lastActiveTime = DateTime.Now;
            while (true)
            {
                if (_cts.IsCancellationRequested)
                {
                    break;
                }

                try
                {
                    SendTimeSync();
                    SendHeartBeat();
                    lastActiveTime = DateTime.Now;

                    CarInfo info;
                    if (_carInfosToBeSend.TryTake(out info))
                    {
                        TvPacket packet;
                        try
                        {
                            packet = TvPacketFactory.CreateDataReportPacket(info);
                            var msg = string.Format("发送车牌SN:" + info.SN);
                            _logger.Info(msg);
                        }
                        catch (Exception ex)
                        {
                            _logger.ErrorException("组装数据上报包出现错误, 该数据包被丢弃", ex);
                            continue;
                        }

                        TlvWriter.Write(_stream, packet);
                        lastActiveTime = DateTime.Now;
                        _carInfosHistory.TryAdd(info.SN, info);
                    }
                    else
                    {
                        System.Threading.Thread.Sleep(3000);
                    }

                    var now = DateTime.Now;
                    if (IsIdleEnough(now, lastActiveTime))
                    {

                        _logger.Info("发送心跳 @ " + now + "thread id: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
                        SendHeartBeat();
                        //SendTimeSync();
                        lastActiveTime = now;
                    }
                }
                catch (IOException ioex)
                {
                    _logger.ErrorException("发送数据包出现io异常, 可能连接已经中断", ioex);
                    //OnConnectionClosed(EventArgs.Empty);
                    //System.Diagnostics.Debug.WriteLine("退出发送线程 id" + System.Threading.Thread.CurrentThread.ManagedThreadId);
                    break;
                }
                catch (Exception ex)
                {
                    _logger.ErrorException("发送数据包出现异常", ex);
                    OnErrorOccurred(new EventArgs<Exception>(ex));
                }
            }
        }

        private bool IsIdleEnough(DateTime now, DateTime lastActiveTime)
        {
            var span = now - lastActiveTime;
            var std = TimeSpan.FromMinutes(.5);
            var res = span >= std;

            System.Diagnostics.Debug.WriteLine(span + "  std:" + std + " verdict: " + res + "threadId: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
            return res;
        }


        private void SendHeartBeat()
        {
            var packet = TvPacketFactory.CreateHeartBeatPacket();
            TlvWriter.Write(_stream, packet);
        }

        private void SendTimeSync()
        {
            var packet = TvPacketFactory.CreateTimeSyncPacket();
            TlvWriter.Write(_stream, packet);
        }
    }
}
